package helper

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/bwmarrin/snowflake"
	"github.com/gin-gonic/gin"
	"log"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
	"yundian/internel/pkg/dingo"
	"yundian/internel/pkg/dingo/global"
	"yundian/internel/pkg/dingo/result"
	"yundian/internel/pkg/utils"
)

// 判断设备型号是否为套餐密码线充
func IsSetMeal(deviceModeId string) bool {
	// XC-PCL-5-001 // 骁匠5键密码线充
	// XC-PCL-5-002  // 天海5键单套餐密码线充
	if deviceModeId == "XC-PCL-5-002" {
		return true
	}
	return false
}

// GetUserIdFromCtx 从上下文中获取用户ID
func GetUserIdFromCtx(ctx *gin.Context) (userId int, err error) {
	uid, exists := ctx.Get("UserId")
	if !exists {
		err = errors.New("用户未登录")
		return
	}
	userId = uid.(int)
	return
}

// GetUserIdFromCtx 从上下文中获取用户ID
func GetUserMessageTypesFromCtx(ctx *gin.Context) (messageTypes []int, err error) {
	d, exists := ctx.Get("MessageTypes")
	if !exists {
		err = errors.New("用户未登录")
		return
	}
	messageTypeStr := d.(string)
	_ = json.Unmarshal([]byte(messageTypeStr), &messageTypes)
	return
}

// GetUserNameFromCtx 从上下文中获取用户名
func GetUserNameFromCtx(ctx *gin.Context) (userName string, err error) {
	uName, exists := ctx.Get("UserName")
	if !exists {
		err = errors.New("用户未登录")
		return
	}
	userName = uName.(string)
	return
}

// GetIsAdminFromCtx 从上下文中获取用户是否为管理员
func GetIsAdminFromCtx(ctx *gin.Context) (isAdmin uint8, err error) {
	isAdm, exists := ctx.Get("IsAdmin")
	if !exists {
		err = errors.New("用户未登录")
		return
	}
	isAdmin = isAdm.(uint8)
	return
}

func GetIsPlaceFromCtx(ctx *gin.Context) (IsPlace uint8, err error) {
	val, exists := ctx.Get("IsPlace")
	if !exists {
		err = errors.New("用户未登录")
		return
	}
	IsPlace = val.(uint8)
	return
}

func GetIsDrawFromCtx(ctx *gin.Context) (IsDraw uint8, err error) {
	val, exists := ctx.Get("IsDraw")
	if !exists {
		err = errors.New("用户未登录")
		return
	}
	IsDraw = val.(uint8)
	return
}

// GetParentIdFromCtx 从上下文中获取用户的上级ID
func GetParentIdFromCtx(ctx *gin.Context) (parentId int, err error) {
	pid, exists := ctx.Get("ParentId")
	if !exists {
		err = errors.New("用户未登录")
		return
	}
	parentId = pid.(int)
	return
}

// GetXmPdPassword 获取密码 密码组蓝牙连接
func GetXmPdPassword(deviceSn string, index int, time int) (pw string, idx int) {
	if index > 49 {
		index = 1
	}

	key := deviceSn[len(deviceSn)-3:]
	typeNum := 1

	if time <= 30 {
		typeNum = 1
	} else if time > 30 && time <= 60 {
		typeNum = 2
	} else if time > 60 && time <= 120 {
		typeNum = 3
	} else if time > 120 && time <= 180 {
		typeNum = 4
	} else {
		typeNum = 5
	}

	if data := GetDeviceXmPdPassword(index, key); data.Err != nil {
		fmt.Println("GetDevicePdPassword-err", data.Err)
	} else {
		pw1 := data.Unwrap().(string)
		pw = fmt.Sprintf(`%s%d%s`, pw1[0:2], typeNum, pw1[2:5])
		idx = index + 1
	}

	return pw, idx
}

// GetPdPassword 获取密码 密码组蓝牙连接
func GetPdPassword(deviceSn string, index int, time int) (pw string, idx int) {
	if index > 25 {
		index = 1
	}

	key := deviceSn[len(deviceSn)-3:]
	typeName := "A"
	typeNum := "1"

	if time <= 30 {
		typeName = "C"
		typeNum = "4"
	} else if time > 30 && time <= 60 {
		typeName = "A"
		typeNum = "1"
	} else if time > 60 && time <= 120 {
		typeName = "A"
		typeNum = "2"
	} else if time > 120 && time <= 180 {
		typeName = "A"
		typeNum = "3"
	} else if time > 120 && time <= 240 {
		typeName = "A"
		typeNum = "4"
	} else if time > 240 && time <= 300 {
		typeName = "A"
		typeNum = "5"
	} else if time > 300 && time <= 360 {
		typeName = "B"
		typeNum = "1"
	} else if time > 360 && time <= 420 {
		typeName = "B"
		typeNum = "2"
	} else if time > 420 && time <= 480 {
		typeName = "B"
		typeNum = "3"
	} else if time > 480 && time <= 540 {
		typeName = "B"
		typeNum = "4"
	} else if time > 540 && time <= 600 {
		typeName = "B"
		typeNum = "5"
	} else if time > 600 && time <= 660 {
		typeName = "C"
		typeNum = "1"
	} else if time > 660 && time <= 720 {
		typeName = "C"
		typeNum = "2"
	} else {
		typeName = "C"
		typeNum = "3"
	}

	if data := GetDevicePdPassword(index, key, typeName); data.Err != nil {
		fmt.Println("GetDevicePdPassword-err", data.Err)
	} else {
		pw = typeNum + data.Unwrap().(string)
		idx = index + 1
	}

	return pw, idx
}

func GetPdPasswordDll(deviceSn string, index int, time int) (pw string, idx int) {
	if index > 49 {
		index = 1
	}

	key := deviceSn[len(deviceSn)-3:]
	typeName := "A"
	typeNum := "1"

	if time <= 30 {
		typeName = "C"
		typeNum = "4"
	} else if time > 30 && time <= 60 {
		typeName = "A"
		typeNum = "1"
	} else if time > 60 && time <= 120 {
		typeName = "A"
		typeNum = "2"
	} else if time > 120 && time <= 180 {
		typeName = "A"
		typeNum = "3"
	} else if time > 120 && time <= 240 {
		typeName = "A"
		typeNum = "4"
	} else if time > 240 && time <= 300 {
		typeName = "A"
		typeNum = "5"
	} else if time > 300 && time <= 360 {
		typeName = "B"
		typeNum = "1"
	} else if time > 360 && time <= 420 {
		typeName = "B"
		typeNum = "2"
	} else if time > 420 && time <= 480 {
		typeName = "B"
		typeNum = "3"
	} else if time > 480 && time <= 540 {
		typeName = "B"
		typeNum = "4"
	} else if time > 540 && time <= 600 {
		typeName = "B"
		typeNum = "5"
	} else if time > 600 && time <= 660 {
		typeName = "C"
		typeNum = "1"
	} else if time > 660 && time <= 720 {
		typeName = "C"
		typeNum = "2"
	} else {
		typeName = "C"
		typeNum = "3"
	}

	if data := GetDevicePdPassword(index, key, typeName); data.Err != nil {
		fmt.Println("GetDevicePdPassword-err", data.Err)
	} else {
		pw = typeNum + data.Unwrap().(string)
		idx = index + 1
	}

	return pw, idx
}

func GetDevicePdPassword(index int, key, typeName string) *dingo.ErrorWrap {
	pw := ``
	db := global.Orm.Table("ble_device_password bdp").Select("bdp.password")
	db.Where("bdp.index=? AND bdp.key=? AND bdp.type=?", index, key, typeName)
	db.Find(&pw)
	return dingo.ErrorIf(db.Error, pw)
}

func GetDeviceXmPdPassword(index int, key string) *dingo.ErrorWrap {
	pw := ``
	db := global.Orm.Table("ble_device_password_dll bdp").Select("bdp.password")
	db.Where("bdp.index=? AND bdp.key=?", index, key)
	db.Find(&pw)
	return dingo.ErrorIf(db.Error, pw)
}

// 获取密码 算法
// deviceSn 设备序列号 , key	密钥, index 密码序号, time	充电时间
func GetPassword(deviceSn string, key string, index int, time int) (pw string, idx int) {
	if index > 20 {
		index = 1 // 超过20组密码，重置密码
	}

	pwdIndex := strconv.Itoa(index)
	// 小于9的时候前面加 '0'
	if index <= 9 {
		pwdIndex = utils.Join("0", pwdIndex)
	}

	//假设密钥串：“RUIOO”，设备编号：“18B1P00001”，序号01的密码计算是：
	//对“RUIOO18B1P0000101“进行MD5值计算，结果为：”2f8712c3fc8605fe1e4c98976c0ffd02“；
	//取最后3位组成16进制数：“d02”；
	//第2位密码：( (0xd02>>9) & 0x7 ) % 5+1=2
	//第3位密码：( (0xd02>>6) & 0x7 ) % 5+1=5
	//第4位密码：( (0xd02>>3) & 0x7 ) % 5+1=1
	//第5位密码：( 0xd02& 0x7) % 5+1=3
	//密码后4位组合为：2513
	//
	//密钥值：可以根据批次改变，后台存储每个批次对应的密钥值即可；
	//密码序号：两位数字，范围是01~20，也就是每个终端设备共配20个密码，减少密码重复概率。
	//算法优势：该算法对相邻的设备编号之间的密码重复概率最小。

	// 加密
	str := utils.Join(key, deviceSn, pwdIndex) //  RUIOO18B1P0000101
	strMd5 := utils.Str2Md5(str)               // 2f8712c3fc8605fe1e4c98976c0ffd02
	strMd5 = string(strMd5[len(strMd5)-3:])    // d02

	val, _ := strconv.ParseInt(strMd5, 16, 64)
	v, _ := strconv.ParseInt("7", 16, 64)

	pwd1 := (val>>9&v)%5 + 1
	pwd2 := (val>>6&v)%5 + 1
	pwd3 := (val>>3&v)%5 + 1
	pwd4 := (val&v)%5 + 1

	password := utils.Join(fmt.Sprintf("%d", pwd1), fmt.Sprintf("%d", pwd2), fmt.Sprintf("%d", pwd3), fmt.Sprintf("%d", pwd4))

	if password == "1111" || password == "2222" || password == "3333" || password == "4444" {
		index = index + 1
		return GetPassword(deviceSn, key, index, time)
	}
	log.Println("打印测试时间", time)
	// 1：20分钟，2：1小时，3：2小时，4：3小时，5：12小时
	if time <= 20 {
		pw = utils.Join("1", password)
	} else if time > 20 && time <= 60 {
		pw = utils.Join("2", password)
	} else if time > 60 && time <= 120 {
		pw = utils.Join("3", password)
	} else if time > 120 && time <= 180 {
		pw = utils.Join("4", password)
	} else if time > 180 && time <= 1440 {
		pw = utils.Join("5", password)
	} else {
		pw = utils.Join("5", password)
	}
	//if time >= 60 && time < 120 {
	//	pw = utils.Join("2", password)
	//} else if time >= 120 && time < 180 {
	//	pw = utils.Join("3", password)
	//} else if time >= 180 && time < 720 {
	//	pw = utils.Join("4", password)
	//} else if time >= 720 && time < 1440 {
	//	pw = utils.Join("5", password)
	//} else if time >= 1440 {
	//	pw = utils.Join("5", password)
	//} else {
	//	pw = utils.Join("1", password)
	//}
	idx = index + 1
	return
}

// 4键密码算法
func GetYCPassword(deviceSn string, key string, index int, time int) (pw string, idx int) {
	if index > 40 {
		index = 1 // 超过40组密码，重置密码
	}

	pwdIndex := strconv.Itoa(index)
	// 小于9的时候前面加 '0'
	if index <= 9 {
		pwdIndex = utils.Join("0", pwdIndex)
	}

	//假设密钥串：“RUIOO”，设备编号：“18B1P00001”，序号01的密码计算是：
	//① 对“RUIOO18B1P0000101“进行MD5值计算，结果为：”2f8712c3fc8605fe1e4c98976c0ffd02“；
	//② 取最后4位组成两个字节的16进制数：“fd02”；
	//③ 第一位密码：( 0xFD& 0x3 ) + 1=2
	//④ 第二位密码：( (0x02>>6) & 0x3 ) + 1=1
	//⑤ 第三位密码：( (0x02>>4) & 0x3 ) + 1=1
	//⑥ 第四位密码：( (0x02>>2) & 0x3 ) + 1=1
	//⑦ 第五位密码：( 0x02 & 0x3 ) + 1=3
	//⑧ 密码组合为：21113

	// 加密
	str := utils.Join(key, deviceSn, pwdIndex) //  RUIOO18B1P0000101
	newStr := utils.Str2Md5(str)               // 2f8712c3fc8605fe1e4c98976c0ffd02
	//strMd5 := string(newStr[len(newStr)-4:])    // fd02
	start := string(newStr[len(newStr)-4 : len(newStr)-2]) // fd
	end := string(newStr[len(newStr)-2:])                  // 02
	//log.Println(strMd5, start, end)

	v3, _ := strconv.ParseInt("3", 16, 64)
	startStr, _ := strconv.ParseInt(start, 16, 64)
	endStr, _ := strconv.ParseInt(end, 16, 64)

	pwd1 := (startStr&v3)%5 + 1
	pwd2 := (endStr>>6&v3)%5 + 1
	pwd3 := (endStr>>4&v3)%5 + 1
	pwd4 := (endStr>>2&v3)%5 + 1
	pwd5 := (endStr&v3)%5 + 1

	log.Println(pwd1, pwd2, pwd3, pwd4, pwd5)

	password := utils.Join(fmt.Sprintf("%d", pwd1),
		fmt.Sprintf("%d", pwd2),
		fmt.Sprintf("%d", pwd3),
		fmt.Sprintf("%d", pwd4),
		fmt.Sprintf("%d", pwd5))

	//if password == "111111" || password == "222222" || password == "333333" || password == "444444" {
	//	index = index + 1
	//	return GetYCPassword(deviceSn, key, index, time)
	//}
	log.Println("打印测试时间", time)
	if time <= 60 { // 1小时
		pw = utils.Join(password, "1")
	} else if time > 60 && time <= 180 { // 3小时
		pw = utils.Join(password, "2")
	} else if time > 180 && time <= 300 { // 5小时
		pw = utils.Join(password, "3")
	} else if time >= 720 && time < 1080 { // 12小时
		pw = utils.Join(password, "4")
	} else {
		pw = utils.Join(password, "4")
	}
	idx = index + 1
	return
}

// 计算使用时长
func UseTimeToHour(useTime int64, payInterval int64) int64 {
	var hour, minute, useHour int64

	day := (useTime) / (24 * 3600)
	hour = ((useTime) - day*3600*24) / 3600
	minute = ((useTime) - day*24*3600 - hour*3600) / 60
	////second = (useTime) - day*24*3600 - hour*3600 - minute*60
	//
	//useHour = minute / payInterval
	//s := minute % payInterval
	//
	//if s > 0 {
	//	useHour = useHour + 1
	//}

	second := int64(0)
	if payInterval == 0 {
		hour = 0
	} else {
		hour = (useTime) / 60 / payInterval // 如每60分钟2元，那么除以60计算出小时
		second = (useTime) % payInterval    // 秒
	}
	log.Println("打印测试--小时，分钟", hour, minute, second)
	if second > 0 {
		useHour = hour + 1
	} else {
		useHour = hour
	}

	log.Printf("打印测试--小时%d，分钟%d, 使用时长 %d", hour, minute, useHour)
	return useHour
	//day := (endTime - startTime) / (24 * 3600)
	//hour = ((endTime - startTime) - day*3600*24) / 3600
	//minute = ((endTime - startTime) - day*24*3600 - hour*3600) / 60
	//second = (endTime - startTime) - day*24*3600 - hour*3600 - minute*60
	//log.Println("打印测试--小时，分钟", hour, minute, second)
	//
	//if hour == 0 { // 1小时以内的,按一小时算
	//	useTime = 1
	//} else { // 大于1小时以上的
	//	if minute > 0 && minute >= 11 { // 大于10分钟+1
	//		useTime = hour + 1
	//	} else { // 免费使用10分钟
	//		useTime = hour
	//	}
	//}
	//return useTime
}

// 创建文件夹
func MakeDir(dir string) error {
	if !FileIsExisted(dir) {
		if err := os.MkdirAll(dir, 0777); err != nil { //os.ModePerm
			fmt.Println("MakeDir failed:", err)
			return err
		}
	}
	return nil
}

func FileIsExisted(filename string) bool {
	existed := true
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		existed = false
	}
	return existed
}

func GenerateUUID() (uuid string) {
	node, err := snowflake.NewNode(1)
	if err != nil {
		uuid = time.Now().String()
	} else {
		uuid = fmt.Sprintf("%s", node.Generate())
	}
	return
}

func GetPhoneStr(phone string) string {
	old := ""
	for k, v := range phone {
		if k >= 3 && k <= 6 {
			old = old + string(v)
		}
	}
	var phoneStr string
	phoneStr = fmt.Sprintf(strings.Replace(phone, old, "****", -1))

	return phoneStr
}

// 获取微信场景值
func GetWeixinSence(senceId string) string {
	var senceDesc string
	switch senceId {
	case "1000":
		senceDesc = "其他"
	case "1001":
		senceDesc = "发现栏小程序主入口，「最近使用」列表"
	case "1005":
		senceDesc = "微信首页顶部搜索框的搜索结果页"
	case "1006":
		senceDesc = "发现栏小程序主入口搜索框的搜索结果页"
	case "1007":
		senceDesc = "单人聊天会话中的小程序消息卡片"
	case "1008":
		senceDesc = "群聊会话中的小程序消息卡片"
	case "1010":
		senceDesc = "收藏夹"
	case "1011":
		senceDesc = "扫描二维码"
	case "1012":
		senceDesc = "长按图片识别二维码"
	case "1013":
		senceDesc = "扫描手机相册中选取的二维码"
	case "1014":
		senceDesc = "小程序订阅消息（与1107相同）"
	case "1017":
		senceDesc = "前往小程序体验版的入口页"
	case "1019":
		senceDesc = "微信钱包"
	case "1020":
		senceDesc = "公众号" // 已废弃
	case "1022":
		senceDesc = "聊天顶部置顶小程序入口" // 已废弃
	case "1023":
		senceDesc = "安卓系统桌面图标"
	case "1024":
		senceDesc = "小程序 profile 页"
	case "1025":
		senceDesc = "扫描一维码"
	case "1026":
		senceDesc = "发现栏小程序主入口，「附近的小程序」列表"
	case "1027":
		senceDesc = "微信首页顶部搜索框搜索结果页「使用过的小程序」列表"
	case "1028":
		senceDesc = "我的卡包"
	case "1029":
		senceDesc = "小程序中的卡券详情页"
	case "1030":
		senceDesc = "自动化测试下打开小程序"
	case "1031":
		senceDesc = "长按图片识别一维码"
	case "1032":
		senceDesc = "扫描手机相册中选取的一维码"
	case "1035":
		senceDesc = "公众号自定义菜单"
	case "1037":
		senceDesc = "小程序打开小程序"
	case "1038":
		senceDesc = "从另一个小程序返回"
	case "1042":
		senceDesc = "添加好友搜索框的搜索结果页"
	case "1043":
		senceDesc = "公众号模板消息"
	case "1047":
		senceDesc = "扫描小程序码"
	case "1048":
		senceDesc = "长按图片识别小程序码"
	case "1049":
		senceDesc = "扫描手机相册中选取的小程序码"
	case "1053":
		senceDesc = "搜一搜的结果页"
	case "1054":
		senceDesc = "顶部搜索框小程序快捷入口" // 已废弃
	case "1059":
		senceDesc = "体验版小程序绑定邀请页"
	case "1089":
		senceDesc = "微信聊天主界面下拉"
	case "1090":
		senceDesc = "长按小程序右上角菜单唤出最近使用历史"
	case "1088":
		senceDesc = "会话中查看系统消息，打开小程序"
	case "1096":
		senceDesc = "1096 	聊天记录，打开小程序"
	case "1100":
		senceDesc = "红包封面详情页打开小程序"
	case "1101":
		senceDesc = "远程调试热更新（开发者工具中，预览 -> 自动预览 -> 编译并预览）"
	case "1102":
		senceDesc = "公众号 profile 页服务预览"
	case "1129":
		senceDesc = "微信爬虫访问"
	case "1131":
		senceDesc = "浮窗（8.0版本起仅包含被动浮窗）"
	case "1106":
		senceDesc = "聊天主界面下拉，从顶部搜索结果页，打开小程序"
	case "1145":
		senceDesc = "发现栏 - 发现小程序"
	case "1167":
		senceDesc = "H5 通过开放标签打开小程序"
	case "1168":
		senceDesc = "移动应用直接运行小程序"
	case "1169":
		senceDesc = "发现栏小程序主入口，各个生活服务入口"
	case "1187":
		senceDesc = "浮窗"
	}
	return senceDesc
}

func ConvSpecStr(str string) string {
	newStr := strings.Split(str, "-")
	lastStr := strings.Join(newStr, "")
	return lastStr
}

func GetWeek(w string) string {
	switch w {
	case "Monday":
		return "星期一"
	case "Tuesday":
		return "星期二"
	case "Wednesday":
		return "星期三"
	case "Thursday":
		return "星期四"
	case "Friday":
		return "星期五"
	case "Saturday":
		return "星期六"
	case "Sunday":
		return "星期日"
	}
	return ""
}

func GetMenuList(module string) result.MenuList {
	menuList := result.NewMenuList()
	if module == "" {
		return menuList
	}
	mList := make([]result.Menu, 0)
	var moduleList []string
	_ = json.Unmarshal([]byte(module), &moduleList)
	for _, lst := range menuList {
		for _, label := range moduleList {
			if lst.Path == label {
				mList = append(mList, lst)
			}
		}
	}
	return mList
}

func RandFloats(min, max float64, n int) []float64 {
	res := make([]float64, n)
	for i := range res {
		res[i] = min + rand.Float64()*(max-min)
	}
	return res
}

func RandInt(min, max, total int64) []int64 {
	var numRes []int64
	var i int64
	for i = 1; i <= total; i++ {
		num := rand.Int63n(max-min) + min
		numRes = append(numRes, num)
	}
	return numRes
}

func InSlice(items []string, item string) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}

func InSliceByInt(items []int, item int) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}
